home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume14 / mush6.0 / part09 < prev    next >
Encoding:
Internet Message Format  |  1988-04-12  |  49.9 KB

  1. From: island!argv@sun.com (Dan Heller)
  2. Subject: Mail User's Shell, version 6.0
  3.  
  4. #! /bin/sh
  5. # This is a shell archive.  Remove anything before this line, then unpack
  6. # it by saving it into a file and typing "sh file".  To overwrite existing
  7. # files, type "sh file -c".  You can also feed this as standard input via
  8. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  9. # will see the following message at the end:
  10. #        "End of archive 9 (of 14)."
  11. # Contents:  loop.c tool_help
  12. # Wrapped by rsalz@fig.bbn.com on Wed Apr 13 20:04:51 1988
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. if test -f 'loop.c' -a "${1}" != "-c" ; then 
  15.   echo shar: Will not clobber existing file \"'loop.c'\"
  16. else
  17. echo shar: Extracting \"'loop.c'\" \(24591 characters\)
  18. sed "s/^X//" >'loop.c' <<'END_OF_FILE'
  19. X/* loop.c     (c) copyright 1986 (Dan Heller) */
  20. X
  21. X/*
  22. X * Here is where the main loop for text mode exists. Also, all the
  23. X * history is kept here and all the command parsing and execution
  24. X * and alias expansion in or out of text/graphics mode is done here.
  25. X */
  26. X
  27. X#include "mush.h"
  28. X
  29. X#ifdef BSD
  30. X#include <sys/wait.h>
  31. X#else
  32. X#ifndef SYSV
  33. X#include <wait.h>
  34. X#endif /* SYSV */
  35. X#endif /* BSD */
  36. X
  37. X#define ever (;;)
  38. X#define MAXARGS        100
  39. X#define isdelimeter(c)    (index(" \t;|", c))
  40. X
  41. char *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  42. char **calloc();
  43. X
  44. struct history {
  45. X    int histno;
  46. X    char **argv;
  47. X    struct history *prev;
  48. X    struct history *next;
  49. X};
  50. static struct history *hist_head, *hist_tail;
  51. struct history *malloc();
  52. X#define NULL_HIST    (struct history *)0
  53. X
  54. static char *last_aliased;
  55. static int hist_size, print_only;
  56. X
  57. do_loop()
  58. X{
  59. X    register char *p, **argv;
  60. X    char      **last_argv = DUBL_NULL, line[256];
  61. X    int         argc, c = (iscurses - 1);
  62. X    struct history *new;
  63. X#ifdef CURSES
  64. X    int          save_echo_flg = FALSE;
  65. X#endif /* CURSES */
  66. X
  67. X    /* catch the right signals -- see main.c for other signal catching */
  68. X    (void) signal(SIGINT, catch);
  69. X    (void) signal(SIGQUIT, catch);
  70. X    (void) signal(SIGHUP, catch);
  71. X    (void) signal(SIGTERM, catch);
  72. X    (void) signal(SIGCHLD, sigchldcatcher);
  73. X    (void) signal(SIGPIPE, SIG_IGN); /* if pager is terminated before end */
  74. X
  75. X    turnoff(glob_flags, IGN_SIGS);
  76. X    if (hist_size == 0) /* if user didn't set history in .rc file */
  77. X    hist_size = 1;
  78. X
  79. X    for ever {
  80. X    if (setjmp(jmpbuf)) {
  81. X        Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  82. X#ifdef CURSES
  83. X        if (c > 0) { /* don't pass last command back to curses_command() */
  84. X        iscurses = TRUE;
  85. X        c = hit_return();
  86. X        }
  87. X#endif /* CURSES */
  88. X    }
  89. X#ifdef CURSES
  90. X    if (iscurses || c > -1) {
  91. X        /* if !iscurses, we know that we returned from a curses-based
  92. X         * call and we really ARE still in curses. Reset tty modes!
  93. X         */
  94. X        if (ison(glob_flags, ECHO_FLAG)) {
  95. X        turnoff(glob_flags, ECHO_FLAG);
  96. X        echo_off();
  97. X        save_echo_flg = TRUE;
  98. X        }
  99. X        if (!iscurses) {
  100. X        iscurses = TRUE;
  101. X        c = hit_return();
  102. X        }
  103. X        if ((c = curses_command(c)) == -1 && save_echo_flg) {
  104. X        echo_on();
  105. X        turnon(glob_flags, ECHO_FLAG);
  106. X        save_echo_flg = FALSE;
  107. X        }
  108. X        continue;
  109. X    }
  110. X#endif /* CURSES */
  111. X    clear_msg_list(msg_list);
  112. X    (void) check_new_mail();
  113. X
  114. X    /* print a prompt according to printf like format:
  115. X     * (current message, deleted, unread, etc) are found in mail_status.
  116. X     */
  117. X    mail_status(1);
  118. X    if (Getstr(line, 256, 0) > -1)
  119. X        p = line;
  120. X    else {
  121. X        if (p = do_set(set_options, "ignoreeof")) {
  122. X        if (!*p)
  123. X            continue;
  124. X        else
  125. X            p = strcpy(line, p); /* so processing won't destroy var */
  126. X        } else {
  127. X        (void) quit(0, DUBL_NULL);
  128. X        continue; /* quit may return if new mail arrives */
  129. X        }
  130. X    }
  131. X
  132. X    skipspaces(0);
  133. X    if (!*p && !(p = do_set(set_options, "newline"))) {
  134. X        (void) readmsg(0, DUBL_NULL, msg_list);
  135. X        continue;
  136. X    }
  137. X    if (!*p) /* if newline is set, but no value, then continue */
  138. X        continue;
  139. X
  140. X    /* upon error, argc = -1 -- still save in history so user can
  141. X     * modify syntax error. if !argv, error is too severe.  We pass
  142. X     * the last command typed in last_argv for history reference, and
  143. X     * get back the current command _as typed_ (unexpanded by aliases
  144. X     * or history) in last_argv.
  145. X     */
  146. X    if (!(argv = make_command(p, &last_argv, &argc)))
  147. X        continue;
  148. X    /* now save the new argv in the newly created history structure */
  149. X    if (!(new = malloc(sizeof (struct history))))
  150. X        error("can't increment history");
  151. X    else {
  152. X        new->histno = ++hist_no;
  153. X        new->argv = last_argv; /* this is the command _as typed_ */
  154. X        new->next = NULL_HIST;
  155. X        new->prev = hist_head;
  156. X        /* if first command, the tail of the list is "new" because
  157. X         * nothing is in the list.  If not the first command, the
  158. X         * head of the list's "next" pointer points to the new command.
  159. X         */
  160. X        if (hist_head)
  161. X        hist_head->next = new;
  162. X        else
  163. X        hist_tail = new;
  164. X        hist_head = new;
  165. X    }
  166. X    /*
  167. X     * truncate the history list to the size of the history.
  168. X     * Free the outdated command (argv) and move the tail closer to front.
  169. X     * use a while loop in case the last command reset histsize to "small"
  170. X     */
  171. X    while (hist_head->histno - hist_tail->histno >= hist_size) {
  172. X        hist_tail = hist_tail->next;
  173. X        free_vec(hist_tail->prev->argv);
  174. X        xfree(hist_tail->prev);
  175. X        hist_tail->prev = NULL_HIST;
  176. X    }
  177. X
  178. X    if (print_only) {
  179. X        print_only = 0;
  180. X        free_vec(argv);
  181. X    } else if (argc > -1)
  182. X        (void) do_command(argc, argv, msg_list);
  183. X    }
  184. X}
  185. X
  186. X/* make a command from "buf".
  187. X * first, expand history references. make an argv from that and save
  188. X * in last_argv (to be passed back and stored in history). After that,
  189. X * THEN expand aliases. return that argv to be executed as a command.
  190. X */
  191. char **
  192. make_command(start, last_argv, argc)
  193. register char *start, ***last_argv;
  194. int *argc;
  195. X{
  196. X    register char *p, **tmp;
  197. X    char buf[BUFSIZ];
  198. X
  199. X    if (!last_argv)
  200. X    tmp = DUBL_NULL;
  201. X    else
  202. X    tmp = *last_argv;
  203. X    /* first expand history -- (here's where argc gets set)
  204. X     * pass the buffer, the history list to reference if \!* (or whatever)
  205. X     * result in static buffer (pointed to by p) -- even if history parsing is
  206. X     * ignored, do this to remove \'s behind !'s and verifying matching quotes
  207. X     */
  208. X    if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > BUFSIZ)
  209. X    return DUBL_NULL;
  210. X    /* if history was referenced in the command, echo new command */
  211. X    if (*argc)
  212. X    puts(buf);
  213. X
  214. X    /* argc may == -1; ignore this error for now but catch it later */
  215. X    if (!(tmp = mk_argv(buf, argc, 0)))
  216. X    return DUBL_NULL;
  217. X
  218. X    /* save this as the command typed */
  219. X    if (last_argv)
  220. X    *last_argv = tmp;
  221. X
  222. X    /* expand all aliases (recursively)
  223. X     * pass _this_ command (as typed and without aliases) to let aliases
  224. X     * with "!*" be able to reference the command line just typed.
  225. X     */
  226. X    if (alias_stuff(buf, *argc, tmp) == -1)
  227. X    return DUBL_NULL;
  228. X
  229. X    /* now, expand variable references and make another argv */
  230. X    if (!variable_expand(buf))
  231. X    return DUBL_NULL;
  232. X
  233. X    if (!last_argv)
  234. X    free_vec(tmp);
  235. X
  236. X    /* with everything expanded, build final argv from new buffer
  237. X     * Note that backslashes and quotes still exist. Those are removed
  238. X     * because argument final is 1.
  239. X     */
  240. X    tmp = mk_argv(buf, argc, 1);
  241. X    return tmp;
  242. X}
  243. X
  244. X/*
  245. X * do the command specified by the argument vector, argv.
  246. X * First check to see if argc < 0. If so, someone called this
  247. X * command and they should not have! make_command() will return
  248. X * an argv but it will set argc to -1 if there's a sytanx error.
  249. X */
  250. do_command(argc, argv, list)
  251. char **argv, list[];
  252. X{
  253. X    register char *p;
  254. X    char **tmp = argv;
  255. X    int i, status;
  256. X    long do_pipe = ison(glob_flags, DO_PIPE);
  257. X
  258. X    turnoff(glob_flags, IS_PIPE);
  259. X
  260. X    if (argc <= 0) {
  261. X    turnoff(glob_flags, DO_PIPE);
  262. X    return -1;
  263. X    }
  264. X
  265. X    clear_msg_list(list);
  266. X
  267. X    for (i = 0; do_pipe >= 0 && argc; argc--) {
  268. X    p = argv[i];
  269. X    if (!strcmp(p, "|") || !strcmp(p, ";")) {
  270. X        if (do_pipe = (*p == '|'))
  271. X        turnon(glob_flags, DO_PIPE);
  272. X        argv[i] = NULL;
  273. X        /* if piping, then don't call next command if this one fails. */
  274. X        if ((status = exec_argv(i, argv, list)) <= -1 && do_pipe) {
  275. X        print("Broken pipe.\n");
  276. X        do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  277. X        }
  278. X        /* if command failed and piping, or command worked and not piping */
  279. X        if (do_pipe <= 0)
  280. X        status = 0, clear_msg_list(list);
  281. X        /* else command worked and piping: set is_pipe */
  282. X        else if (!status)
  283. X        turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  284. X        argv[i] = p;
  285. X        argv += (i+1);
  286. X        i = 0;
  287. X    } else
  288. X        i++;
  289. X    }
  290. X    if (do_pipe >= 0)
  291. X    status = exec_argv(i, argv, list);
  292. X    Debug("freeing: "), print_argv(tmp);
  293. X    free_vec(tmp);
  294. X    turnoff(glob_flags, DO_PIPE);
  295. X    return status;
  296. X}
  297. X
  298. exec_argv(argc, argv, list)
  299. register char **argv, list[];
  300. X{
  301. X    register int n;
  302. X
  303. X    if (!argv || !*argv || **argv == '\\' && !*++*argv) {
  304. X    if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  305. X        print("Invalid null command.\n");
  306. X    return -1;
  307. X    }
  308. X    Debug("executing: "), print_argv(argv);
  309. X
  310. X    /* if interrupted during execution of a command, return -1 */
  311. X    if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  312. X    Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  313. X    return -1;
  314. X    }
  315. X
  316. X    /* standard commands */
  317. X    for (n = 0; cmds[n].command; n++)
  318. X    if (!strcmp(argv[0], cmds[n].command))
  319. X        return (*cmds[n].func)(argc, argv, list);
  320. X
  321. X    /* ucb-Mail compatible commands */
  322. X    for (n = 0; ucb_cmds[n].command; n++)
  323. X    if (!strcmp(argv[0], ucb_cmds[n].command))
  324. X        return (*ucb_cmds[n].func)(argc, argv, list);
  325. X
  326. X    /* for hidden, undocumented commands */
  327. X    for (n = 0; hidden_cmds[n].command; n++)
  328. X    if (!strcmp(argv[0], hidden_cmds[n].command))
  329. X        return (*hidden_cmds[n].func)(argc, argv, list);
  330. X
  331. X#ifdef SUNTOOL
  332. X    /* check tool-only commands */
  333. X    if (istool)
  334. X    for (n = 0; fkey_cmds[n].command; n++)
  335. X        if (!strcmp(argv[0], fkey_cmds[n].command))
  336. X        return (*fkey_cmds[n].func)(argc, argv);
  337. X#endif /* SUNTOOL */
  338. X
  339. X    if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  340. X            && (n = get_msg_list(argv, list)) != 0) {
  341. X    if (n > 0 && isoff(glob_flags, DO_PIPE))
  342. X        for (n = 0; n < msg_cnt; n++)
  343. X        if (msg_bit(list, n)) {
  344. X            display_msg((current_msg = n), (long)0);
  345. X            unset_msg_bit(list, n);
  346. X        }
  347. X    return 0;
  348. X    } else if (strlen(*argv) == 1 && index("$^.", **argv)) {
  349. X    if (!msg_cnt)
  350. X        print("No messages.");
  351. X    else {
  352. X        if (**argv != '.')
  353. X        current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  354. X        set_msg_bit(list, current_msg);
  355. X        display_msg(current_msg, (long)0);
  356. X    }
  357. X    return 0;
  358. X    }
  359. X    /* get_msg_list will set the current message bit if nothing parsed */
  360. X    unset_msg_bit(list, current_msg);
  361. X
  362. X    if (!istool && do_set(set_options, "unix")) {
  363. X    if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  364. X        print("There is no piping to or from UNIX commands.\n");
  365. X    else
  366. X        execute(argv);  /* try to execute a unix command */
  367. X    return -1; /* doesn't affect messages! */
  368. X    }
  369. X
  370. X    print("%s: command not found.\n", *argv);
  371. X    if (!istool)
  372. X    print("type '?' for valid commands, or type `help'\n");
  373. X    return -1;
  374. X}
  375. X
  376. X/* recursively look for aliases on a command line.  aliases may
  377. X * reference other aliases.
  378. X */
  379. alias_stuff(b, argc, Argv)
  380. register char     *b, **Argv;
  381. X{
  382. X    register char     *p, **argv = DUBL_NULL;
  383. X    register int     n = 0, i = 0, Argc;
  384. X    static int         loops;
  385. X    int         dummy;
  386. X
  387. X    if (++loops == 20) {
  388. X    print("Alias loop.\n");
  389. X    return -1;
  390. X    }
  391. X    for (Argc = 0; Argc < argc; Argc++) {
  392. X    register char *h = Argv[n + ++i];
  393. X    register char *p2 = "";
  394. X
  395. X    /* we've hit a command separator or the end of the line */
  396. X    if (h && strcmp(h, ";") && strcmp(h, "|"))
  397. X        continue;
  398. X
  399. X    /* create a new argv containing this (possible subset) of argv */
  400. X    if (!(argv = calloc((unsigned)(i+1), sizeof (char *))))
  401. X        continue;
  402. X    while (i--)
  403. X        strdup(argv[i], Argv[n+i]);
  404. X
  405. X    if ((!last_aliased || strcmp(last_aliased, argv[0]))
  406. X            && (p = alias_expand(argv[0]))) {
  407. X        /* if history was referenced, ignore the rest of argv
  408. X         * else copy all of argv onto the end of the buffer.
  409. X         */
  410. X        if (!(p2 = hist_expand(p, argv, &dummy)))
  411. X        break;
  412. X        if (!dummy)
  413. X        (void) argv_to_string(p2+strlen(p2), argv+1);
  414. X        if (Strcpy(b, p2) > BUFSIZ) {
  415. X        print("Not enough buffer space.\n");
  416. X        break;
  417. X        }
  418. X        /* release old argv and build a new one based on new string */
  419. X        free_vec(argv);
  420. X        if (!(argv = mk_argv(b, &dummy, 0)))
  421. X        break;
  422. X        if (alias_stuff(b, dummy, argv) == -1)
  423. X        break;
  424. X    } else
  425. X        b = argv_to_string(b, argv);
  426. X    xfree(last_aliased), last_aliased = NULL;
  427. X    free_vec(argv);
  428. X    b += strlen(b);
  429. X    if (h) {
  430. X        p2 = h;
  431. X        while (++Argc < argc && (h = Argv[Argc]))
  432. X        if (strcmp(h, ";") && strcmp(h, "|"))
  433. X            break;
  434. X        b += strlen(sprintf(b, " %s ", p2));
  435. X        n = Argc--;
  436. X    }
  437. X    i = 0;
  438. X    }
  439. X    xfree(last_aliased), last_aliased = NULL;
  440. X    --loops;
  441. X    if (Argc < argc) {
  442. X    free_vec(argv);
  443. X    return -1;
  444. X    }
  445. X    return 0;
  446. X}
  447. X
  448. char *
  449. alias_expand(cmd)
  450. register char *cmd;
  451. X{
  452. X    register char *p;
  453. X    register int x;
  454. X
  455. X    if (!(p = do_set(functions, cmd)))
  456. X    return NULL;
  457. X    last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  458. X    if (isoff(glob_flags, WARNING))
  459. X    return p;
  460. X    for (x = 0; cmds[x].command; x++)
  461. X    if (!strcmp(cmd, cmds[x].command)) {
  462. X        wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  463. X        return p;
  464. X    }
  465. X    for (x = 0; ucb_cmds[x].command; x++)
  466. X    if (!strcmp(cmd, ucb_cmds[x].command)) {
  467. X        wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  468. X        return p;
  469. X    }
  470. X    return p;
  471. X}
  472. X
  473. X/* expand history references and separate message lists from other tokens */
  474. char *
  475. hist_expand(str, argv, hist_was_referenced)
  476. register char *str, **argv;
  477. register int *hist_was_referenced;
  478. X{
  479. X    static char   buf[BUFSIZ];
  480. X    register int  b = 0, inquotes = 0;
  481. X    int       first_space = 0, ignore_bang;
  482. X
  483. X    ignore_bang = (ison(glob_flags, IGN_BANG) ||
  484. X           do_set(set_options, "ignore_bang"));
  485. X
  486. X    if (hist_was_referenced)
  487. X    *hist_was_referenced = 0;
  488. X    while (*str) {
  489. X    while (!inquotes && isspace(*str))
  490. X        str++;
  491. X    do  {
  492. X        if (!*str)
  493. X        break;
  494. X        if (b >= BUFSIZ-1) {
  495. X        print("argument list too long.\n");
  496. X        return NULL;
  497. X        }
  498. X        if ((buf[b] = *str++) == '\'') {
  499. X        /* make sure there's a match! */
  500. X        inquotes = !inquotes;
  501. X        }
  502. X        if (!first_space && !inquotes && index("0123456789{}*$", buf[b])
  503. X                 && b && !index("0123456789{}- \t", buf[b-1])) {
  504. X        buf[b+1] = buf[b];
  505. X        buf[b++] = ' ';
  506. X        while ((buf[++b] = *str++) && index("0123456789-,${}", buf[b]))
  507. X            ;
  508. X        if (!buf[b])
  509. X            str--;
  510. X        first_space++;
  511. X        }
  512. X        /* check for (;) (|) or any other delimeter and separate it from
  513. X         * other tokens.
  514. X         */
  515. X        if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  516. X        (b < 0 || buf[b-1] != '\\')) {
  517. X        if (b && !isspace(buf[b-1]))
  518. X            buf[b+1] = buf[b], buf[b++] = ' ';
  519. X        b++;
  520. X        break;
  521. X        }
  522. X        /* if double-quotes, just copy byte by byte, char by char ... */
  523. X        if (buf[b] == '"') {
  524. X        int B = b;
  525. X        while ((buf[++B] = *str++) && buf[B] != '"')
  526. X            ;
  527. X        if (buf[B])
  528. X            b = B;
  529. X        else
  530. X            str--;
  531. X        b++;
  532. X        continue;
  533. X        }
  534. X        if (buf[b] == '\\') {
  535. X        if ((buf[++b] = *str) == '!')
  536. X            buf[--b] = '!';
  537. X        ++str;
  538. X        } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  539. X            && !ignore_bang) {
  540. X        char word[BUFSIZ];
  541. X        if (!(str = reference_hist(str, word, argv)))
  542. X            return NULL;
  543. X        if (hist_was_referenced)
  544. X            *hist_was_referenced = 1;
  545. X        if (strlen(word) + b >= BUFSIZ) {
  546. X            print("argument list too long.\n");
  547. X            return NULL;
  548. X        }
  549. X        b += Strcpy(&buf[b], word) - 1;
  550. X        }
  551. X        b++;
  552. X    } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  553. X    if (!inquotes)
  554. X        first_space++, buf[b++] = ' ';
  555. X    }
  556. X    buf[b] = 0;
  557. X    return buf;
  558. X}
  559. X
  560. X/*
  561. X * find mush variable references and expand them to their values.
  562. X * variables are preceded by a '$' and cannot be within single
  563. X * quotes.  Only if expansion has been made do we copy buf back into str.
  564. X * RETURN 0 on failure, 1 on success.
  565. X */
  566. variable_expand(str)
  567. register char *str;
  568. X{
  569. X    register int     b = 0;
  570. X    char             buf[BUFSIZ], *start = str;
  571. X    int             expanded = 0;
  572. X
  573. X    while (*str) {
  574. X    if (*str == '~' && (str == start || isspace(*(str-1)))) {
  575. X        register char *p = any(str, " \t"), *tmp;
  576. X        int x = 1;
  577. X        if (p)
  578. X        *p = 0;
  579. X        tmp = getpath(str, &x);
  580. X        /* if error, print message and return 0 */
  581. X        if (x == -1) {
  582. X        wprint("%s: %s\n", str, tmp);
  583. X        return 0;
  584. X        }
  585. X        b += Strcpy(buf+b, tmp);
  586. X        if (p)
  587. X        *p = ' ', str = p;
  588. X        else
  589. X        str += strlen(str);
  590. X        expanded = 1;
  591. X    }
  592. X    /* if single-quotes, just copy byte by byte, char by char ... */
  593. X    if ((buf[b] = *str++) == '\'') {
  594. X        while ((buf[++b] = *str++) && buf[b] != '\'')
  595. X        ;
  596. X        if (!buf[b])
  597. X        str--;
  598. X    }
  599. X    /* If $ is eoln, continue.  Variables must start with a `$'
  600. X     * and continue with {, _, a-z, A-Z or it is not a variable.      }
  601. X     */
  602. X    if (buf[b] == '$' &&
  603. X        (isalpha(*str) || *str == '{' || *str == '_'))  /* } */  {
  604. X        register char c, *p, *var, *end;
  605. X
  606. X        if (*(end = var = str) == '{')  /* } */   {
  607. X        if (!isalpha(*++str) && *str != '_') {
  608. X            print("Illegal variable name.\n");
  609. X            return 0;
  610. X        }
  611. X        if (!(end = index(var, '}'))) /* { */   {
  612. X            print("Unmatched '{'.\n"); /* } */
  613. X            return 0;
  614. X        }
  615. X        *end++ = 0;
  616. X        } else
  617. X        while (isalnum(*++end) || *end == '_')
  618. X            ;
  619. X        /* advance "str" to the next parse-point, replace the end
  620. X         * of "var" (end) with a null, and save char in `c'
  621. X         */
  622. X        c = *(str = end), *end = 0;
  623. X
  624. X        /* get the value of the variable. */
  625. X        if (p = do_set(set_options, var))
  626. X        b += Strcpy(buf+b, p);
  627. X        else {
  628. X        print("%s: undefined variable\n", var);
  629. X        return 0;
  630. X        }
  631. X        expanded = 1;
  632. X        *str = c; /* replace the null with the old character */
  633. X    } else
  634. X        b++;
  635. X    }
  636. X    buf[b] = 0;
  637. X    if (expanded) /* if any expansions were done, copy back into orig buf */
  638. X    (void) strcpy(start, buf);
  639. X    return 1;
  640. X}
  641. X
  642. X/* make an vector of space delimeted character strings out of string "str".
  643. X * place in "argc" the number of args made. If final is true, then remove
  644. X * quotes and backslants according to standard.
  645. X */
  646. char **
  647. mk_argv(str, argc, final)
  648. register char *str;
  649. register int *argc;
  650. X{
  651. X    register char    *s, *p;
  652. X    register int    tmp, err = 0;
  653. X    char        *newargv[MAXARGS], **argv, *p2, c;
  654. X
  655. X    *argc = 0;
  656. X    while (*str) {
  657. X    while (isspace(*str))
  658. X        ++str;
  659. X    if (*str) {        /* found beginning of a word */
  660. X        s = p = str;
  661. X        do  {
  662. X        if (p - s >= BUFSIZ-1) {
  663. X            print("argument list too long.\n");
  664. X            return DUBL_NULL;
  665. X        }
  666. X        if ((*p = *str++) == '\\') {
  667. X            if (final && (*str == ';' || *str == '|'))
  668. X            /* make ";" look like " ;" */
  669. X            *p = ' ';
  670. X            if (*++p = *str) /* assign and compare to NULL */
  671. X            str++;
  672. X            continue;
  673. X        }
  674. X        if (p2 = index("\"'", *p)) {
  675. X            register char c2 = *p2;
  676. X            /* you can't escape quotes inside quotes of the same type */
  677. X            if (!(p2 = index(str, c2))) {
  678. X            if (final)
  679. X                print("Unmatched %c.\n", c2);
  680. X            err++;
  681. X            p2 = str;
  682. X            }
  683. X            tmp = (int)(p2 - str) + 1; /* take upto & include quote */
  684. X            (void) strncpy(p + !final, str, tmp);
  685. X            p += tmp - 2 * final; /* change final to a boolean */
  686. X            if (*(str = p2))
  687. X            str++;
  688. X        }
  689. X        } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  690. X        if (c = *str) /* set c = *str, check for null */
  691. X        str++;
  692. X        *p = 0;
  693. X        if (*s) {
  694. X        newargv[*argc] = savestr(s);
  695. X        (*argc)++;
  696. X        }
  697. X        *p = c;
  698. X    }
  699. X    }
  700. X    if (!*argc)
  701. X    return DUBL_NULL;
  702. X    /* newargv[*argc] = NULL; */
  703. X    if (!(argv = calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  704. X    perror("mk_argv: calloc");
  705. X    return DUBL_NULL;
  706. X    }
  707. X    for (tmp = 0; tmp < *argc; tmp++)
  708. X    argv[tmp] = newargv[tmp];
  709. X    if (err)
  710. X    *argc = -1;
  711. X    return argv;
  712. X}
  713. X
  714. X/*
  715. X * reference previous history from syntax of str and place result into buf
  716. X * We know we've got a history reference -- we're passed the string starting
  717. X * the first char AFTER the '!' (which indicates history reference)
  718. X */
  719. char *
  720. reference_hist(str, buf, hist_ref)
  721. register char *str, *buf, **hist_ref;
  722. X{
  723. X    int        relative = *str == '-'; /* relative from current hist_no */
  724. X    int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  725. X    register char  *p, **argv = hist_ref;
  726. X    struct history *hist;
  727. X
  728. X    buf[0] = 0;
  729. X    if (index("!:$*", *str)) {
  730. X    old_hist = hist_no;
  731. X    if (*str == '!')
  732. X        str++;
  733. X    } else if (isdigit(*(str + relative)))
  734. X    str = my_atoi(str + relative, &old_hist);
  735. X    else if (!(p = hist_from_str(str, &old_hist)))
  736. X    return NULL;
  737. X    else
  738. X    str = p;
  739. X    if (relative)
  740. X    old_hist = (hist_no-old_hist) + 1;
  741. X    if (old_hist == hist_no) {
  742. X    if (!(argv = hist_ref))
  743. X        print("You haven't done anything yet!\n");
  744. X    } else {
  745. X    if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  746. X        old_hist <= 0) {
  747. X        if (old_hist <= 0)
  748. X        print("You haven't done that many commands, yet.\n");
  749. X        else
  750. X        print("Event %d %s.\n", old_hist,
  751. X            (old_hist > hist_no)? "hasn't happened yet": "expired");
  752. X        return NULL;
  753. X    }
  754. X    hist = hist_head;
  755. X    while (hist && hist->histno != old_hist)
  756. X        hist = hist->prev;
  757. X    if (hist)
  758. X        argv = hist->argv;
  759. X    }
  760. X    if (!argv)
  761. X    return NULL;
  762. X    while (argv[argend+1])
  763. X    argend++;
  764. X    lastarg = argend;
  765. X    if (*str && index(":$*-", *str)) {
  766. X    int isrange;
  767. X    if (*str == ':' && isdigit(*++str))
  768. X        str = my_atoi(str, &argstart);
  769. X    if (isrange = (*str == '-'))
  770. X        str++;
  771. X    if (!isdigit(*str)) {
  772. X        if (*str == 'p')
  773. X        str++, print_only = 1;
  774. X        else if (!*str || isdelimeter(*str))
  775. X        if (isrange)
  776. X            argend--; /* unspecified end of range implies last-1 arg */
  777. X        else
  778. X            argend = argstart; /* no range specified; use arg given */
  779. X        else {
  780. X        if (*str == '*')
  781. X            if (argv[0])
  782. X            argstart = 1, argend = ++lastarg;
  783. X            else
  784. X            argstart = 0;
  785. X        else if (*str == '$' && !isrange)
  786. X            argstart = argend;
  787. X        else if (*str != '$')
  788. X            print("%c: unknown arguement selector.\n", *str);
  789. X        str++;
  790. X        }
  791. X    } else
  792. X        str = my_atoi(str, &argend);
  793. X    }
  794. X    if (argstart > lastarg || argend > lastarg || argstart > argend) {
  795. X    print("Bad argument selector.\n");
  796. X    return NULL;
  797. X    }
  798. X    while (argstart <= argend) {
  799. X    n += Strcpy(&buf[n], argv[argstart++]);
  800. X    buf[n++] = ' ';
  801. X    }
  802. X    buf[--n] = 0;
  803. X    return str;
  804. X}
  805. X
  806. X/* find a history command that contains the string "str"
  807. X * place that history number in "hist" and return the end of the string
  808. X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  809. X * in the second example, return the pointer to "bar"
  810. X */
  811. char *
  812. hist_from_str(str, hist_number)
  813. register char *str;
  814. register int *hist_number;
  815. X{
  816. X    register char *p = NULL, c = 0;
  817. X    int       full_search = 0, len, found;
  818. X    char       buf[BUFSIZ];
  819. X    struct history *hist;
  820. X#ifndef REGCMP
  821. X    extern char   *re_comp();
  822. X#else
  823. X    extern char   *regcmp();
  824. X#endif /* REGCMP */
  825. X
  826. X    if (*str == '?') {
  827. X    if (p = index(++str, '?'))
  828. X        *p++ = 0;
  829. X    else
  830. X        p = str + strlen(str);
  831. X    full_search = 1;
  832. X    } else if (*str == '{')
  833. X    if (!(p = index(str, '}'))) {   /* { */
  834. X        print("Unmatched '}'");
  835. X        return NULL;
  836. X    } else
  837. X        *p++ = 0, ++str;
  838. X    else
  839. X    p = str;
  840. X    while (*p && *p != ':' && !isspace(*p))
  841. X    p++;
  842. X    c = *p, *p = 0;
  843. X    if (*str) {
  844. X#ifndef REGCMP
  845. X    if (re_comp(str))
  846. X#else
  847. X    if (!regcmp(str, NULL))
  848. X#endif /* REGCMP */
  849. X        return NULL;
  850. X    } else {
  851. X    *hist_number = hist_no;
  852. X    return p;
  853. X    }
  854. X    len = strlen(str);
  855. X    /* move thru the history in reverse searching for a string match. */
  856. X    for (hist = hist_head; hist; hist = hist->prev) {
  857. X    if (full_search) {
  858. X        (void) argv_to_string(buf, hist->argv);
  859. X        Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  860. X    }
  861. X    if (!full_search) {
  862. X        (void) strcpy(buf, hist->argv[0]);
  863. X        Debug("Checking for (%s) in (#%d: %*s)\n",
  864. X        str, hist->histno, len, buf);
  865. X        found = !strncmp(buf, str, len);
  866. X    } else
  867. X        found =
  868. X#ifndef REGCMP
  869. X        re_exec(buf)
  870. X#else
  871. X        !!regex(str, buf, NULL) /* convert to boolean value */
  872. X#endif /* REGCMP */
  873. X                == 1;
  874. X    if (found) {
  875. X        *hist_number = hist->histno;
  876. X        Debug("Found it in history #%d\n", *hist_number);
  877. X        *p = c;
  878. X        return p;
  879. X    }
  880. X    }
  881. X    print("%s: event not found\n", str);
  882. X    return NULL;
  883. X}
  884. X
  885. disp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  886. register int n;
  887. char **argv;
  888. X{
  889. X    register int    list_num = TRUE, num_of_hists = hist_size;
  890. X    register int    reverse = FALSE;
  891. X    struct history    *hist = hist_tail;
  892. X
  893. X    while (*++argv && *argv[0] == '-') {
  894. X    n = 1;
  895. X    do  switch(argv[0][n]) {
  896. X        case 'h': list_num = FALSE;
  897. X        when 'r': reverse = TRUE; hist = hist_head;
  898. X        otherwise: print("usage: history [-h] [-r] [#histories]\n");
  899. X               return -1;
  900. X        }
  901. X    while (argv[0][++n]);
  902. X    }
  903. X    if (*argv)
  904. X    if (!isdigit(**argv)) {
  905. X        print("history: badly formed number\n");
  906. X        return -1;
  907. X    } else
  908. X        num_of_hists = atoi(*argv);
  909. X
  910. X    if (num_of_hists > hist_size || num_of_hists > hist_no)
  911. X    num_of_hists = min(hist_size, hist_no);
  912. X
  913. X    if (!reverse)
  914. X    while (hist_no - hist->histno > num_of_hists) {
  915. X        printf("skipping %d\n", hist->histno);
  916. X        hist = hist->next;
  917. X    }
  918. X
  919. X    do_pager(NULL, TRUE);
  920. X    for (n = 0; n < num_of_hists && hist; n++) {
  921. X    char buf[256];
  922. X    if (list_num)
  923. X        do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  924. X    (void) argv_to_string(buf, hist->argv);
  925. X    (void) do_pager(buf, FALSE);
  926. X    if (do_pager("\n", FALSE) == -1)
  927. X        break;
  928. X    hist = (reverse)? hist->prev : hist->next;
  929. X    }
  930. X    do_pager(NULL, FALSE);
  931. X    return -1;
  932. X}
  933. X
  934. init_history(newsize)
  935. X{
  936. X    if ((hist_size = newsize) < 1)
  937. X    hist_size = 1;
  938. X}
  939. END_OF_FILE
  940. if test 24591 -ne `wc -c <'loop.c'`; then
  941.     echo shar: \"'loop.c'\" unpacked with wrong size!
  942. fi
  943. # end of 'loop.c'
  944. fi
  945. if test -f 'tool_help' -a "${1}" != "-c" ; then 
  946.   echo shar: Will not clobber existing file \"'tool_help'\"
  947. else
  948. echo shar: Extracting \"'tool_help'\" \(23733 characters\)
  949. sed "s/^X//" >'tool_help' <<'END_OF_FILE'
  950. X@(#)tool_help    (c) copyright 10/18/86 (Dan Heller)
  951. X
  952. X%general%
  953. X
  954. X      IF ALL ELSE FAILS, READ THE DIRECTIONS!
  955. X
  956. This famous  quote applies here more  than ever. If
  957. you are unfamiliar with this mailtool, get yourself
  958. acquainted with it by choosing  HELP options in all
  959. menu items. If you get frustrated or confused about
  960. how to use or run a command, or if you want to know
  961. how something  works or get to know quick shortcuts
  962. in achieving tasks, it is  advisable to look at the
  963. Help option available with the item.
  964. X
  965. Since there  are many different  options to some of
  966. the commands in  mailtool, explanation  of  options
  967. for the commands can be found by choosing the RIGHT
  968. mouse button over an item. This will display a menu
  969. of options for the command. One of the menu options
  970. will almost always be a Help option.
  971. X
  972. Give yourself a head start, try selecting this same
  973. item with the RIGHT mouse button.  When you do, you
  974. will given some more help topics to choose from.
  975. X%%
  976. X
  977. X%help%
  978. Help was designed for users to get  help from anywhere on  the mailtool
  979. window.  The RIGHT mouse button may be selected on virtually every -
  980. X on any of the windows on the entire tool and  a menu will appear.
  981. The last item in almost  every menu  is a "help" item.  You will get an
  982. appropriate help message describing what you can do at the position you
  983. are in on the mailtool window.
  984. X
  985. If a help  message isn't much help, it may be more helpful to reference
  986. a different help item which  describes in more detail  what you want to
  987. know.  For example, reading the help for  "folder" will help you better
  988. understand  the method in  which mail messages are stored than it would
  989. if you had read the help message for "save" first.
  990. X%%
  991. X
  992. X%mouse%
  993. The mouse is an image (cursor) which moves across the screen. Its
  994. position indicates which window is to receive input when you type
  995. or click a mouse button.
  996. X
  997. The mouse may take upon  different images which  indicate various
  998. things. When the image looks like a "coffee cup",  Mushtool is in
  999. the process of doing something,  like sending mail, or reading in
  1000. new mail.  In this event, you should wait till the cursor returns
  1001. to its normal state before attempting to do anything else. Go get
  1002. some coffee.
  1003. X
  1004. When the mouse looks like a pair of glasses, you are reading mail
  1005. and when it looks like a pencil, you  are editing a letter.  When
  1006. in the  Header Window, the cursor will look like the mouse device
  1007. that you hold  with the buttons  flashing on and off.  This is to
  1008. remind  you that you  can use each button  to do different tasks.
  1009. X
  1010. In one window, the Main Panel Window, the cursor image looks like
  1011. an envelope.  Placing the cursor over "Panel Items" and selecting
  1012. the LEFT button will do that command.  Selecting the RIGHT  mouse
  1013. button will give a menu of options to choose from. In most cases,
  1014. Help is  available and the end of each menu list to help you with
  1015. the proper use of Panel Items.
  1016. X
  1017. When you are asked a yes or no question, choosing either the LEFT
  1018. or RIGHT mouse buttons is the same as typing "y" or "n".
  1019. X%%
  1020. X
  1021. X%respond%
  1022. This item responds to mail in 4 ways.
  1023. In all cases, at least one recipient of your message will be the
  1024. sender of the message you are responding to. If a subject was in
  1025. the author's letter, then it will be used as your subject.
  1026. X
  1027. The first and most used method of response is to the author of the
  1028. message only.  Selecting this item with the LEFT mouse button will
  1029. use this method for responding to mail.
  1030. X
  1031. If you want to include a copy of the author's message, then choose
  1032. the menu item which  says to include the message.  If you wish for
  1033. all the recipients of the message to receive a copy of your reply,
  1034. then choosing the third item will include them.
  1035. X
  1036. The fourth menu item will mail to the author and everyone listed on
  1037. the To and Cc lines of the message, and include the message you are
  1038. responding to in your text.
  1039. X
  1040. In such cases where you include the message you are responding to,
  1041. the included message will be indented by "> " to identify it from
  1042. your message. If you would like to have a string other than the
  1043. default used, then set the appropriate option to whatever you would
  1044. prefer by selecting the "Opts" item, moving the mouse on top of the
  1045. string, "indent-str", selecting the LEFT mouse button and typing the
  1046. desired string.
  1047. X%%
  1048. X
  1049. X%menu_respond%
  1050. When you respond to a message using the menu item, you respond to the author
  1051. of the message only. Since there are  more ways to respond to a message, you
  1052. may want to choose the  Main Panel Window's Reply item. This item also gives
  1053. far more extensive help than described here.
  1054. X%%
  1055. X
  1056. X%menu_delete%
  1057. This menu may delete or undelete
  1058. whichever  message you happen to
  1059. have the cursor sitting on.  For
  1060. deleting messages  only, you can
  1061. simply  select the  MIDDLE mouse
  1062. button  over the message  header
  1063. you would like deleted.
  1064. X
  1065. For extensive information on -
  1066. X or undeletion of messages
  1067. try the the  Main Panel Window's
  1068. item for Delete.
  1069. X%%
  1070. X
  1071. X%delete%
  1072. You may  delete or undelete  messages with this item.
  1073. When using the LEFT mouse button, you will delete the
  1074. current message (HIGHLIGHTED in the headers' window).
  1075. Otherwise, you may select the menu item for undelete.
  1076. X
  1077. You may  delete or undelete a  "range" of messages by
  1078. typing the range in the Header Window.
  1079. X
  1080. For help on valid  message ranges, select the menu in
  1081. the area  marked "range" in the  Header Panel Window.
  1082. X%%
  1083. X
  1084. X%folder%
  1085. This item changes your "folder" -- which is  a place to keep all your
  1086. individual messages. Usually, if you are a heavy mail user, you would
  1087. organize your mail in such a way in which related mail would be saved
  1088. together in one folder.   You create folders simply by saving mail to
  1089. a filename.  Additional mail can  be saved to those files in the same
  1090. way.  To manipulate messages in folders,  you "change folders" to the
  1091. folder you wish to access using the folder item.   Since it may occur
  1092. that you switch  back and forth  between two folders, you may use the
  1093. previous  folder menu item which updates changes  made to the current
  1094. folder and  changes your  folder to the one previous to  the current.
  1095. X
  1096. You may also select  the exact name of the  folder you wish to access
  1097. by selecting  the left  mouse button on the "folder"  item and TYPING
  1098. the exact  name of the folder  you wish to access.  The "pathname" to
  1099. the folder may start with a tilde (~) indicating your home directory.
  1100. Or, it may  contain a plus sign before the  name indicating your Mail
  1101. directory (+reports, for example).  Alternatively, you  can type  '%'
  1102. to access your system Mailbox, the place where all your mail is first
  1103. delivered.  And finally, you can type '#' to indicate  the previously
  1104. accessed folder.  See the help for "chdir" for more information.
  1105. X%%
  1106. X
  1107. X%chdir%
  1108. This is used to just change working directories.
  1109. Your  working directory contains files and other
  1110. directories.  Files can be "mail folders"  which
  1111. contain mail  messages.  You can change to other
  1112. directories using some of the following methods:
  1113. X
  1114. You can select from the menu, HOME or Mail, which
  1115. are your home and default mail directories. Or,
  1116. select the left mouse button and TYPE in the name
  1117. of the directory you would like to change to.
  1118. X
  1119. Typed names may have the following syntax:
  1120. X
  1121. X~[/subpath]   will change to your home directory and
  1122. X              a path below that, if specified. Also,
  1123. X              you can specify other users: ~username
  1124. X+[subpath]    This is your default Mail directory.
  1125. X%%
  1126. X
  1127. X%save%
  1128. You may save messages in  two ways. The most commonly used method is to
  1129. save messages to  your mailbox folder  ("mbox") in your home directory.
  1130. If you use mail very frequently and save large amounts of mail, you can
  1131. save messages to other folders for better organization.
  1132. X
  1133. Usually, when messages are saved, mushtool  marks them for deletion for
  1134. the next update.  If you don't want to have saved messages deleted, you
  1135. must undelete them or set the variable "keepsave" in the options screen.
  1136. X
  1137. There is a text item  in the Main Panel Window which allows you to type
  1138. the  name of the file to save a message.  Select the  LEFT mouse button
  1139. over the "Save" item, and type the filename and hit return. If there is
  1140. no filename specified, then messages are saved to your mbox file.
  1141. X
  1142. You can specify a range, or group of messages to save by typing a range
  1143. in the  Header Panel Window.  If there is a  message list in that panel
  1144. item, then the range of messages specified there will be saved. If not,
  1145. the current message will be saved.
  1146. X
  1147. For additional information, see the help option for Folders.
  1148. X%%
  1149. X
  1150. X%quit%
  1151. There are various ways in which you may be finished with mailtool.
  1152. The most commonly used method  is to simply "close" the tool to an
  1153. iconic form. This means that you haven't really quit, but you have
  1154. merely put it on "hold" till later.  It will become an icon on the
  1155. side or corner of the screen and appear to sit and do nothing.  To
  1156. close the tool to icon form, there are two methods which have will
  1157. have two different effects.
  1158. X
  1159. The first method is to select  this panel item with the left mouse
  1160. button.  This  will update your current  folder (deleting messages
  1161. marked for deletion) and close the tool.  The second method  is to
  1162. use the tool manager around the perimeter of the window and select
  1163. X"close".  This will close the tool without updating your mailfile.
  1164. X
  1165. Whenever the mailtool is  in the "closed"  state, it  periodically
  1166. checks your mail and updates your folder with the  new mail. While
  1167. mailtool is in iconic form, it will display the number of messages
  1168. you have in the current folder.
  1169. X
  1170. There are two equally similar methods of exiting  mailtool, rather
  1171. than just closing to an icon:  you may select the second menu item
  1172. in the menu given by this panel item or you may use the tool mana-
  1173. X "quit" item.
  1174. X
  1175. Using the tool manager's quit will exit the tool without updating
  1176. your folder whereas the panel item's menu selection will have the
  1177. mailtool prompt you whether to update the current folder or not.
  1178. X%%
  1179. X
  1180. X%help_menu_help_msg%
  1181. Selecting an item within this menu will
  1182. give you help on that item. If you want
  1183. to execute the action, choose the other
  1184. menu by placing the mouse over the menu
  1185. BEHIND this menu, continue to keep your
  1186. RIGHT mouse button depressed and select
  1187. the LEFT mouse button  over the menu on
  1188. the bottom and select that action.
  1189. X%%
  1190. X
  1191. X%msg_menu%
  1192. When given menu in the Header Window, you will have a choice
  1193. of actions to take.  The message may not be the current one,
  1194. it may be  any message that  appears in  the Headers Window.
  1195. The "title" of the menu  will indicate which message you are
  1196. referring to.
  1197. X
  1198. At this point, you can select actions to take. You can Read,
  1199. Delete, Undelete, Save, Reply to, or Print messages. Most of
  1200. these are self explanatory, but if you need help with one of
  1201. these, place the  mouse over the menu BEHIND the given menu,
  1202. continue to have the RIGHT mouse button depressed and select
  1203. the LEFT mouse button over the Help Menu.
  1204. X
  1205. This action toggles the menus such  that you can change back
  1206. and forth between these menus. The menu you are on will tell
  1207. which action to take on that message.  In  either case,  you
  1208. place the mouse over the action to take, and,  if you are on
  1209. the help menu, help will be given regarding that  particular
  1210. action. If not in  the help menu,  then that specific action
  1211. will actually be taken.
  1212. X%%
  1213. X
  1214. X%edit%
  1215. Choosing this item with the LEFT mouse button in
  1216. the Main Panel Window or in the Menu item will
  1217. allow you to access a full-screen editor. The
  1218. editor which you will use is indicated when you
  1219. select the "opts" item in the Main Panel Window.
  1220. X
  1221. While you are typing a letter, you can specify
  1222. explicitly which editor to use by typing (on a
  1223. line by itself) "~v editor". Type "~?" on a line
  1224. by itself while typing to see a list of valid
  1225. X"~commands".
  1226. X
  1227. Upon exiting the editor, you can continue typing
  1228. and even reenter the editor if you like in the
  1229. same manner.
  1230. X%%
  1231. X
  1232. X%update%
  1233. This item will update the current folder you are using.
  1234. Changes are updated to the folder; that is, deleted mail
  1235. is removed and all other mail is copied back to the folder
  1236. unless otherwise specified. See the help in "folder" for
  1237. more information on folders.
  1238. X
  1239. If new mail has arrived, it will incorporate it. Otherwise,
  1240. new mail is incorporated every two minutes or so, if some
  1241. comes in.
  1242. X%%
  1243. X
  1244. X%headers%
  1245. The message headers are displayed in their own separate window.
  1246. The "current" message is usually displayed in either BOLD or
  1247. REVERSE text. This "highlighted" message is the one which is
  1248. displayed at the bottom, larger window. In the message window,
  1249. each message is displayed in the following format:
  1250. the message number is displayed first; if it is the "current"
  1251. message, then there is a '>' sign.
  1252. The next character is the 'status' character:
  1253. X    'N' -- New (and unread)
  1254. X    'U' -- not new, but still Unread
  1255. X    '*' -- delete messages (set show_deleted)
  1256. X    'P' -- preserve in spoolfile.
  1257. X    'O' -- Old message which has also been Read.
  1258. If there is just a space (no character), the message is new, but
  1259. you've already read it. You should explicitly save or delete these.
  1260. X
  1261. Following that is the Author of the message and/or all or part of
  1262. his network address and login name.  Following that is the number
  1263. of lines the message is. In quotes is all or part of the "Subject"
  1264. X(if one was specified).
  1265. X
  1266. To read a message, select either the READ item in the main panel
  1267. subwindow or move the mouse over the message header you want to read
  1268. and press the LEFT mouse button. Or, the MIDDLE mouse button will
  1269. delete that message. Choosing the RIGHT mouse button will give you
  1270. a menu of things to do then. Included in the menu, is a help item
  1271. which describes the selections in the menu.
  1272. X%%
  1273. X
  1274. X%preserve%
  1275. Usually, after you read mail and you "update" or quit  mailtool, unread
  1276. messages are copied back into your system mailbox, deleted messages are
  1277. removed, and messages which have been read but not deleted are saved in
  1278. your "mbox" file.  Specifying "hold" prevents this from ever happening,
  1279. but you can mark specific messages to be held in your system mailbox by
  1280. preserving them.
  1281. X%%
  1282. X
  1283. X%compose%
  1284. When you start to compose a letter for mailing,
  1285. you will be prompted  for the login name(s), of
  1286. whom  you want to mail, the (optional)  subject
  1287. of the message, and an  optional list of carbon
  1288. copy recipients.  This is an additional list of
  1289. login names  who will be  mailed copies of your
  1290. message.
  1291. X
  1292. After that, anything you type will be added to
  1293. your  message.  If you select the  RIGHT mouse
  1294. button in the window in  which you are type to
  1295. get a menu of  things to do.  You may enter an
  1296. editor if your message needs to be modified in
  1297. more detail.
  1298. X
  1299. When you're through with your message, you can
  1300. send it by typing (on a line by itself) "." or
  1301. X^D.  Or, you can select  the Send item  in the
  1302. Main Panel Window  and your mail will be sent.
  1303. You cannot send mail while still in an editor;
  1304. you must exit the editor first.
  1305. X
  1306. If you have the option "autoedit" set, you are
  1307. automatically put into an editor when you want
  1308. to compose or  whenever you reply to a letter.
  1309. In this case, whenever  you're through editing
  1310. the letter, you will be put back into the main
  1311. editing mode  where you terminate and send the
  1312. letter using any of the above methods.
  1313. X%%
  1314. X
  1315. X%next%
  1316. You can page through all your messages by selecting  "Next" after reading
  1317. each message. The same effect is gotten when you select the "Delete" item
  1318. when the option,  "autoprint" is set to be true  (see "opts") except that
  1319. the current message is deleted before the next one is displayed. Deleting
  1320. mail which is  not important  helps the  efficiency of mailtool and reduces
  1321. unnecessary use of system resources.
  1322. X
  1323. In the Header Window, you will notice the cursor looks like the mouse you
  1324. use.  The blinking buttons on the mouse image remind you that you can use
  1325. any of the  three buttons  at any  time.  When you  move the mouse over a
  1326. message and choose a button, the message under the  mouse is going to be
  1327. the one affected.  Choosing left button will read the message, the middle
  1328. button will delete it, and the right button will give you a menu.
  1329. X%%
  1330. X
  1331. X%aliases%
  1332. Aliases are used as a method of mailing to users with long addresses using
  1333. short names. For example, if you wanted to mail to
  1334. X    argv@spam.istc.sri.com
  1335. but didn't want to type that all the time, then you could make an alias by
  1336. selecting the alias menu item that specifies "adding alias" and then TYPE:
  1337. X    Dan argv@spam.istc.sri.com
  1338. If you want to mail to a list of people and do so frequently enough to want
  1339. an alias name for the whole list, then  you would type something like this:
  1340. X    project-group fred mary bob@foo-bar herb sly@baz.bitnet
  1341. X
  1342. To mail to an "alias" you would compose a letter and address the letter:
  1343. X
  1344. To: Dan
  1345. Subject: Alias example
  1346. Cc: project-group
  1347. X(rest of letter)
  1348. X%%
  1349. X
  1350. X%alts%
  1351. X"Alternates" are alternate names for YOU.  In messages you
  1352. receive, your account will appear on the "To" or "Cc" list.
  1353. When you REPLY to those messages, mailtool will construct
  1354. a message header for your letter which will contain the To
  1355. and Cc lists of recipients from the original message. You
  1356. would probably want your name taken off the list so you do
  1357. not mail yourself a copy of your own message. If you have
  1358. other account names or accounts on other machines, you can
  1359. let mailtool know what those mail addresses are so they can
  1360. be removed from the lists as well.
  1361. X
  1362. Note, that if YOU add your name MANUALLY (type it yourself)
  1363. to either of the lists, it will not be removed.
  1364. X
  1365. You can set such a list in your .mailrc file in your home
  1366. directory by adding the line:
  1367. X
  1368. alts hostname1 hostname2 ...
  1369. X
  1370. If you prefer to not have your name removed from lists when
  1371. responding to mail, set the option "metoo" and this prevents
  1372. the need for alternates and your name will never be removed.
  1373. X%%
  1374. X
  1375. X%opts%
  1376. To set or unset options and their values, move the mouse over
  1377. the option of your choice and select the LEFT button to toggle
  1378. true/false values. If an option requires a string value, you
  1379. must type the value, so select the LEFT button to reference
  1380. the option, and then type away.  Use a Carriage Return to enter
  1381. the final value for the option.
  1382. X
  1383. You may select the RIGHT mouse button anywhere in the window
  1384. to give a menu which consists of saving options permanently,
  1385. reading in previous settings (from ~/.mailrc), and other things.
  1386. X%%
  1387. X
  1388. X%ignore%
  1389. When reading mail, the message "headers" may clutter up the
  1390. window with information you are not interested in.  For
  1391. example, you may not be interested in the "Received" or
  1392. X"Message-Id" field of the mail message. You would find that
  1393. in time, it will become annoying to see these uninteresting
  1394. message headers.
  1395. X
  1396. You can specify which message headers should not be shown,
  1397. thus shortening the appearance of the length of the message.
  1398. X
  1399. Typical settings:
  1400. X    Received
  1401. X    Message-Id
  1402. X    Status
  1403. X%%
  1404. X
  1405. X%printer%
  1406. This item will send the current message, or the message specified on the
  1407. menu header, to the printer. The printer used is given by the "printer"
  1408. option (see opts in Main Panel Window). To specify a different printer,
  1409. change the printer option by selecting the item "Opts" in the Main Panel
  1410. Window, moving the mouse over the Printer option, selecting the LEFT
  1411. mouse button and typing the name of the printer which you'd like to use.
  1412. X
  1413. Be sure to set this option before printing because the DEFAULT option may
  1414. not be what you want.
  1415. X
  1416. You can print messages that are NOT the current message by moving the
  1417. mouse into the Header Window and selecting the RIGHT mouse button on top
  1418. of the message you want to print and selecting the PRINT menu option.
  1419. X%%
  1420. X
  1421. X%windows%
  1422. X"Windows" are the boxes which contain items, text, or graphic
  1423. images.  There are two "panel" windows. A panel window is one
  1424. which contains items,  which are the little boxes  with words
  1425. that you can place the mouse over and click the left or mouse
  1426. button.
  1427. X
  1428. Each  window has a separate  function for different purposes.
  1429. Starting at the top of the main "tool" (contains all windows)
  1430. we have the "header panel." Everything in this panel pertains
  1431. to the message headers, only.  The panel in the middle of the
  1432. tool is the "mail panel" which is more general and applies to
  1433. just about everything. For a description of each of the items
  1434. within any panel, select the  "Help"  option  from  the  menu
  1435. you get by selecting the RIGHT mouse button.
  1436. X%%
  1437. X
  1438. X%options%
  1439. Move the cursor over the option you wish to change.
  1440. The LEFT mouse button turns toggles values off and on.
  1441. The MIDDLE mouse button displays the meaning of a
  1442. variable. If the option needs typed input, use the LEFT
  1443. mouse button.  Sometimes value can be both toggle and
  1444. string values so you may have to click the left button
  1445. more than once to type.  When entering text values, you
  1446. must use RETURN, so the value you typed will be associated
  1447. with the option specified. Unsetting the option will -
  1448. associate the a string value with that option.
  1449. X
  1450. Once values are disassociated with options, they can
  1451. only be retrieved by either reentering their values or
  1452. by selecting the "restore options" item in the menu.
  1453. Doing so will read in the last copy of the saved options
  1454. from your .mailrc file.
  1455. X
  1456. Selecting the Save option in the menu will save the
  1457. current settings in ~/.mailrc.  Selecting the quit
  1458. option in the menu does NOT imply that values are
  1459. saved permanently; changed values will remain through-
  1460. out the rest of the mail session.  To save options more
  1461. permanently, select the save menu item.
  1462. X%%
  1463. X
  1464. X%function keys%
  1465. Selecting the panel item "Opts" with the MENU button will give you
  1466. a choice of the type of options you can set. If you choose the one
  1467. that says "function keys", then you can edit the commands that the
  1468. function keys on the keyboard may execute.  Once in this mode, you
  1469. will find more extensive help.
  1470. X%%
  1471. X
  1472. X%fkeys%
  1473. Usually the LAST function key in each set (top, left,
  1474. and right set of keys) will display the current settings
  1475. of all they keys.  The command which does this is 
  1476. X`key_settings X' where X is L, R, or T (left right top)
  1477. referencing the associated function keys.  To set a function
  1478. key to a specific command or set of commands, place the mouse
  1479. over the icon image of the key on the screen and click the
  1480. left mouse button.  Type a command from the list of commands
  1481. at the bottom of the window and enter RETURN.
  1482. X
  1483. Many commands take arguments or flags, so be sure to enter
  1484. them here if you want those options. Clicking the middle button
  1485. will display the current value for that key.  If you want to
  1486. set a key for multiple commands, separate the commands with
  1487. semicolons:
  1488. X
  1489. L9:  update ; close
  1490. X
  1491. This example would update your mailbox (committing changes)
  1492. and close the tool to an icon.
  1493. X%%
  1494. X
  1495. X%message range%
  1496. You can specify a large group of messages using a combination of special
  1497. symbols in addition to numbers.  For example, if you wish to save all of
  1498. the messages, then you can use `*' to represent them all. If you were to
  1499. type the  "star" and select the Save  menu option for "save range", then
  1500. you would save ALL the messages you have (including deleted ones).
  1501. X
  1502. If you would like to save messages 4 through 9, then you would specify:
  1503. X4-9
  1504. If you want to specify the messages between 2 and 32 except for messages
  1505. X6, 8 and message 12-14, you would type:
  1506. X2-32 {6,8,12-14}
  1507. Commas or spaces can be used to separate numbers.
  1508. X
  1509. Note that you cannot specify negated messages without first specifying
  1510. normal messages; e.g. {2-5} 1-11   doesn't make sense.
  1511. X%%
  1512. X
  1513. X%sort%
  1514. Sorting messages can   be accomplished by  selecting one  of the
  1515. menu items in this panel item.  By default (using the LEFT mouse
  1516. button),  sorting is  done by  message status.  New messages are
  1517. first, followed  by unread messages,  old/read messages, replied
  1518. to  messages,  and  finally deleted messages.  You may also sort
  1519. messages by author, date, or subject by selecting the menu item.
  1520. X%%
  1521. END_OF_FILE
  1522. if test 23733 -ne `wc -c <'tool_help'`; then
  1523.     echo shar: \"'tool_help'\" unpacked with wrong size!
  1524. fi
  1525. # end of 'tool_help'
  1526. fi
  1527. echo shar: End of archive 9 \(of 14\).
  1528. cp /dev/null ark9isdone
  1529. MISSING=""
  1530. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  1531.     if test ! -f ark${I}isdone ; then
  1532.     MISSING="${MISSING} ${I}"
  1533.     fi
  1534. done
  1535. if test "${MISSING}" = "" ; then
  1536.     echo You have unpacked all 14 archives.
  1537.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1538. else
  1539.     echo You still need to unpack the following archives:
  1540.     echo "        " ${MISSING}
  1541. fi
  1542. ##  End of shell archive.
  1543. exit 0
  1544.